#define FUSE_USE_VERSION  26
   
#include <file_cache.h>
#include <delete_timer.h>
#include <flache_debug.h>
#include <flache_rsync.h>
#include <flache_cleaner.h>
//#include <server_check.h>
#include <connection_monitor.h>

#include <fuse.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

static int cancel_delete_thread = FALSE;
//static int cancel_server_check_thread = FALSE;

static struct flache_vol_t flache_vol;
static struct file_entry_list_t* file_list;
static struct timer_thread_info thread_info;

static pthread_t timer_thread;
static pthread_t server_check_thread;

static struct timer_thread_info thread_info;

inline static void lock_write() { pthread_mutex_lock( &flache_vol.write_lock ); }
inline static void unlock_write() { pthread_mutex_unlock( &flache_vol.write_lock ); }
inline static void lock_open() { pthread_mutex_lock( &flache_vol.open_lock ); }
inline static void unlock_open() { pthread_mutex_unlock( &flache_vol.open_lock ); }

/* 
follow this mutex locking sequence:
    lock_open
    lock_write
    lock_list
    lock_entry
    ... unlock in reverse order
*/

/*************************************************
 * constructs a full path based on the path to the
 * server and the provided relative path from the
 * server root
 */
static char* build_path( char* root_path, const char* rel_path )
{
    int path_length = strlen( rel_path ) + strlen( root_path );

    // create a string large enough for the full path and NULL
    char* full_path = (char*)malloc( path_length + 1 );

    // if allocated, then build string
    if ( full_path != NULL )
    {
        // clear it.
        memset( full_path, 0, path_length + 1 );
        // build it.
        strcat( full_path, root_path );
        strcat( full_path, rel_path );
    }

    return full_path;
}

static int is_connected()
{
    lock_write();
    if ( flache_vol.is_connected )
    {
        if ( 0 != check_availability( flache_vol.server_path ) )
        {
            flache_vol.is_connected = FALSE;
            if ( DEBUG )
                write_output( "is_connected(): detected server disconnected" );
        }
    }
    unlock_write();
    
    return flache_vol.is_connected;
}

int server_cmd( int result )
{
    lock_write();

    if( ( result == ENOTCONN || result == ECONNABORTED )
        && flache_vol.is_connected )
    {
        flache_vol.is_connected = FALSE;
        write_output( "server_cmd: detected server disconnected" );
    }

    unlock_write(); 

    return result;
}

static int file_exists( char* path )
{
    struct stat st;

    int result = !stat(path, &st);

    if ( 0 != result )
        server_cmd( errno );

    return result;
}


static int copy_file( char* src, char* dest )
{
    int result = ENOTCONN;
    
    lock_write();

    if ( is_connected() )
    {
        // If we're copying to the flash drive, 
        // verify that there's enough space
        if ( strstr( dest, flache_vol.flash_path ) != NULL )
        {
            struct stat st;

            if ( 0 == stat( src, &st ) )
            {
                if ( 0 == check_flash_space( (size_t)st.st_size, &flache_vol, file_list ) )
                {
                    result = perform_rsync( src, dest, FALSE );
                    if ( 0 != result )
                        server_cmd( check_availability( flache_vol.server_path ) );
                }
                else
                    result = EFBIG; // TODO: is this appropriate?
            }
            else
                result = errno;
        }
        else
        {
            result = perform_rsync( src, dest, FALSE );
            if ( 0 != result )
                server_cmd( check_availability( flache_vol.server_path ) );
        }
    }

    unlock_write();

    return result; 
}

/*
 * starting at relative (from root) path, rel_path, this function
 * recursively scans the directories, picking up the files and
 * adding them to file_list. 
 * scan_dir assumes rel_path leads with "/". Thus, the relative
 * root path is "/".
 */
static void scan_dir( char* rel_path )
{
    DIR* root;
    struct dirent* dir_entry = NULL;

    char full_path[strlen(flache_vol.flash_path ) + strlen( rel_path ) + 1];
    sprintf( full_path, "%s%s",flache_vol.flash_path, rel_path );

    root = opendir( full_path );

    if ( root != NULL )
    {

        for ( errno = 0, dir_entry = readdir( root );
              NULL != dir_entry || errno != 0;
              errno = 0, dir_entry = readdir( root ) )
        {
            // if couldn't read this item, skip it
            if ( NULL == dir_entry )
                continue;

            char entry_rel_path[strlen(rel_path) + strlen(dir_entry->d_name) + 2];

            if ( 1 == strlen( rel_path ) ) // if only "/", then just append entry name
                sprintf( entry_rel_path, "%s%s", rel_path, dir_entry->d_name );
            else
                sprintf( entry_rel_path, "%s/%s", rel_path, dir_entry->d_name );

            if ( DT_DIR != dir_entry->d_type )
            {
                // locks the file if it's in the list
                struct file_entry_t* file = get_file( file_list, entry_rel_path );
                
                if( file == NULL )
                {
                    // create a new file
                    file = create_entry( entry_rel_path, -1, &flache_vol );
                    remove_handle( file, -1 );
                    file->time_released = time( NULL );
                    
                    char* flash_path = build_path( flache_vol.flash_path, entry_rel_path );
                    char* server_path = build_path( flache_vol.server_path, entry_rel_path );

                    // if sync fails, then mark as dirty
                    if ( copy_file( flash_path, server_path ) )
                        file->dirty = TRUE;
                    
                    free( flash_path );
                    free( server_path );

                    // add it to the list
                    lock_list( file_list );
                    add_file( file_list, file );
                    unlock_list( file_list );
                }
                else
                    // unlock after get_file locked it
                    unlock_entry( file );
            }
            // recursively scan, unless . or ..
            else if ( 0 != strcmp( ".", dir_entry->d_name ) && 
                      0 != strcmp( "..", dir_entry->d_name ) )
            {
                scan_dir( entry_rel_path );
            }
        } // end for
    } // end if ( dir != NULL )

    closedir( root );
}

static int flache_unlink(const char *path)
{
    int result;

    char* server_path = build_path( flache_vol.server_path, path );
    char* flash_path = build_path( flache_vol.flash_path, path );

    if ( DEBUG )
        write_output2( "unlink: ", (char*)path );

    if ( is_connected() )
    {
        lock_write();

        // try to remove from server
        result = -server_cmd( unlink( server_path ) );
        
        // if successful, remove from flash
        if ( 0 == result )
            unlink( flash_path );
        
        unlock_write();
    }
    else
        result = -ENOTCONN;

    free( server_path );
    free( flash_path );

    if ( 0 != result && DEBUG )
        report_error( "unlink", result );

    return result;
}

static int flache_getattr(const char *path, struct stat *stbuf)
{
    struct stat stat_buf;

    int result = 0;   
    char* server_path = build_path( flache_vol.server_path, path );
    char* flash_path = build_path( flache_vol.flash_path, path );

    if ( DEBUG )
        write_output2( "getattr: ", (char*)path );

    // if file exists on flash, gettr it, otherwise, gettattr on 
    // the server, if connected
    if ( file_exists( flash_path ) )
        result = ( stat( flash_path, &stat_buf ) == 0 ) ? 0 : -errno;
    
    else
    {
        if ( is_connected() )
        {
            result = stat( server_path, &stat_buf ); 
            if ( result != 0 )
            {
                result = -server_cmd( errno );
                if( result == -ENOTCONN || result == -ECONNABORTED )
                    result = -ENOENT;
            }
        }
        else
            // NOTE; this is to "trick" touch
            result = -ENOENT;
    }

    if ( 0 == result )
    {
        memset( stbuf, 0, sizeof(struct stat) );

        stbuf->st_mode = stat_buf.st_mode;
        stbuf->st_nlink = stat_buf.st_nlink;
        stbuf->st_uid = stat_buf.st_uid;
        stbuf->st_gid = stat_buf.st_gid;
        stbuf->st_size = stat_buf.st_size;
        stbuf->st_atime = stat_buf.st_atime;
        stbuf->st_mtime = stat_buf.st_mtime;
        stbuf->st_ctime = stat_buf.st_ctime;
    }

    if ( result != 0 )
        report_error( "getattr: ", result );         
    
    free( server_path );
    free( flash_path );

    return result;
}

static int flache_chmod( const char * path, mode_t mode ) 
{
    char* server_path = build_path( flache_vol.server_path, path );
    char* flash_path = build_path ( flache_vol.flash_path, path );
    int result = 0; 

    if ( file_exists( flash_path ) )
    {
        result = chmod( flash_path, mode );

        if ( 0 != result )
            result = -errno;

        if ( is_connected() )        
            chmod( server_path, mode );
    }
    else
    { 
        if ( is_connected() )
            result = chmod( server_path, mode );
        else
            result = ENOTCONN;

        if ( 0 != result )
            result = -errno;
    }

    if ( DEBUG )
        write_output2( "chmod: ", (char*)path );

    if ( DEBUG && 0 != result )
        report_error( "chmod: ", result );

    free( server_path );
    free( flash_path );

    return result;   
}

static int flache_utime( const char * path, struct utimbuf* times ) 
{
    char* server_path = build_path( flache_vol.server_path, path );
    char* flash_path = build_path ( flache_vol.flash_path, path );
    int result = 0;

    if ( file_exists( flash_path ) )
    {
        result = -utime( flash_path, times );

        // TODO: should this be subject to server command, etc.
        utime( server_path, times );
    }
    else
        result = -utime( server_path, times );

    if ( DEBUG )
        write_output2( "utime: ", (char*)path );

    if ( DEBUG && 0 != result )
        report_error( "utime: ", result );

    free( server_path );
    free( flash_path );

    return result;
}

static int flache_opendir( const char* path, struct fuse_file_info* fi )
{
    (void) fi; 
    int result = 0;  

    char* server_path = build_path( flache_vol.server_path, path );
    char* flash_path = build_path( flache_vol.flash_path, path );

    DIR *dp = NULL;
    
    // try to open the directory on the flash drive
    dp = opendir( flash_path );

    if ( NULL == dp && ENOENT == errno )
    {   // if not successful, try on server only if connected
        if ( is_connected() )
        {
            dp = opendir( server_path );

            if ( NULL == dp )
                result = -server_cmd( errno );
        }
        else
            result = -ENOTCONN;
    }
    else if ( NULL == dp )
        result = -errno;

    if ( NULL != dp )
        closedir( dp );

    if ( DEBUG )
        write_output2( "opendir: ", (char*)path );

    if( 0 != result )
        report_error( "opendir: ", result );

    free( server_path );
    free( flash_path );

    return result;
}

static int flache_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi)
{
    (void) offset;
    (void) fi;
    int result = -1;
    char* cur_path;

    char* server_path = build_path( flache_vol.server_path, path );
    char* flash_path = build_path( flache_vol.flash_path, path );    

    if ( DEBUG )
        write_output2( "readdir: ", (char*)path );

    if ( is_connected() )
    {
        cur_path = server_path;  
        struct stat st;
        result = stat( cur_path, &st );
        if( result != 0)
            result = errno;                        
        result = -server_cmd( result );            
    }

    // don't make this an "else" because it must run on a 
    // disconnect from the above block
    if ( !flache_vol.is_connected )
    {    
        cur_path = flash_path;
        struct stat st;        
        result = stat( cur_path, &st );
    }
    
    if( 0 == result )
    {
        struct dirent **drent;
        int n;

        n = scandir( cur_path, &drent, 0, alphasort);

        while(n--)
        {
            filler(buf, drent[n]->d_name, NULL, 0);
            free(drent[n]);
        }
            
        free(drent);
    }
    else
        result = -errno;

    if ( result != 0 && DEBUG )
        report_error( "readdir: ", result );

    free(server_path);
    free(flash_path);

    return result;
}

static int flache_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *file_info)
{
    int handle = (int)file_info->fh;
    int result = 0;

    if ( DEBUG )
        write_output2( "read: ", (char*)path );
    
    // This will lock the file
    struct file_entry_t* file = get_file( file_list, (char*)path );

    if ( NULL == file || !contains_handle( file, handle ) )
    {
        if ( NULL != file )
            unlock_entry( file );

        result = -EBADF; // bad file descriptor
    }
    else
    {
        size_t bytes_read = -ENOENT;

        if ( offset == lseek( handle, offset, SEEK_SET ) )
            bytes_read = read( handle, buf, size );

        unlock_entry( file );

        result = (int)bytes_read;
    }

    if ( result != (int)size && DEBUG )
        report_error( "read: ", result );

    return result;
}

static int flache_write( const char *path, const char *wbuf, size_t size, off_t offset, struct fuse_file_info *file_info )
{
    int handle = (int)file_info->fh;
    int result = 0;

    if ( DEBUG )
        write_output2( "write: ", (char*)path );
    
    lock_write();
 
    // This will lock the file
    struct file_entry_t* file = get_file( file_list, (char*)path );
    
    if ( NULL == file || !contains_handle( file, handle ) )
    {
        if ( NULL != file )
            unlock_entry( file );

        result = -EBADF; // bad file descriptor
    }
    else
    {
        if ( 0 == check_flash_space( size, &flache_vol, file_list ) ) 
        {
            if ( offset == lseek( handle, offset, SEEK_SET ) )
            {
                size_t bytes_written = write( handle, wbuf, size );
                
                // Mark the file as dirty - it should not be deleted until
                // it's been synced.
                if ( bytes_written > 0 )
                    file->dirty = TRUE;

                result = (int)bytes_written;
            }
            else
                result = -errno;
        }
        else
            result = -ENOSPC;

        unlock_entry( file );
    }

    unlock_write();

    if ( result != (int)size  )
        report_error( "write: ", result );

    return result;
}

static int flache_rename( const char* old, const char* new )
{
    int result = 0;
    char* old_flash_path = build_path( flache_vol.flash_path, old );
    char* new_flash_path = build_path( flache_vol.flash_path, new );
    char* old_server_path = build_path( flache_vol.server_path, old );
    char* new_server_path = build_path( flache_vol.server_path, new );

    if ( DEBUG )
    {
        char msg[ strlen( old ) + strlen( new ) + 5 ];
        sprintf( msg, "%s to %s", old, new );
        write_output2( "rename: ", msg );
    }

    lock_write();

    // This has a "delete" component to it - the old name is 
    // removed from the file system. Thus, try to rename it on 
    // the server first. Only if that succeeds should you try
    // on the flash drive. 
    if ( is_connected() )
    {
        struct file_entry_t* file = get_file( file_list, (char*)old );

        if ( NULL == file || file->handle_count == 0 )
        {
            result = -server_cmd( rename( old_server_path, new_server_path ) );

            // Note: the flash rename fails silently.
            if ( 0 == result && 0 == rename( old_flash_path, new_flash_path ) && file != NULL )
            {
                rename_entry( file, new );                
                file->time_released = time( NULL );
            }
        }
        
        if ( file != NULL )
            unlock_entry( file );
    }
    else 
        result = -ENOTCONN;

    unlock_write();

    if ( 0 != result )
        report_error( "rename: ", result );       

    free( old_server_path );
    free( new_server_path );
    free( old_flash_path );
    free( new_flash_path );

    return result;
}



static int flache_mknod(const char *path, mode_t mode, dev_t rdev)
{
    int result = 0;
    char* server_path = build_path( flache_vol.server_path, path );
    char* flash_path = build_path( flache_vol.flash_path, path );
    
    if ( DEBUG )
        write_output2( "mknod: ", (char*)path );

    lock_write();

    if ( !file_exists( flash_path ) && !file_exists( server_path ) )
    {
        // create the file with the given mode
        int file = open( flash_path, O_RDONLY | O_CREAT, mode );

        // if file is non-negative, then return 0, else return -1
        result = ( file >= 0 ? 0 : -1 );

        // if successful
        if ( file >= 0 )
        {
            close( file );

            // if synced, we want the file to be cleaned
            struct file_entry_t* entry = get_file( file_list, (char*)path );

            if ( entry == NULL )
            {
                entry = create_entry( (char*)path, -1, &flache_vol );
                remove_handle( entry, -1 );
                entry->time_released = time( NULL );
                entry->dirty = TRUE;
                lock_entry( entry );
                add_file( file_list, entry );
            }

            // create it on the server; if good, mark as clean, if not, do nothing
            if ( is_connected() )
            {
                file = open( server_path, O_RDONLY | O_CREAT, mode );

                if ( file >= 0 )
                    entry->dirty = FALSE;
                else
                    server_cmd( errno );
                    
                close( file );
            }

            unlock_entry( entry );
        }
    }

    unlock_write();
       
    free( server_path );
    free( flash_path );

    if ( 0 != result && DEBUG )
        report_error( "mknod: ", result );

    return result;
}

static int flache_mkdir(const char *path , mode_t mode)
{
    char* flash_path = build_path( flache_vol.flash_path, path );
    char* server_path = build_path( flache_vol.server_path, path );

    write_output2( "mkdir: ", (char*)path);

    // ?? which should be returned? 
    int result = mkdir( flash_path, mode );
    if ( is_connected() )
        server_cmd( mkdir( server_path, mode) );

    free( server_path );
    free( flash_path );

    if ( 0 != result && DEBUG )
        report_error( "mkdir: ", result );

    return result;
}

static int flache_rmdir(const char *path)
{
    int result = 0;
    char* flash_path = build_path( flache_vol.flash_path, path );    
    char* server_path = build_path( flache_vol.server_path, path );

    if ( DEBUG )
        write_output2( "rmdir: ", (char*)path);

    lock_write();

    if ( is_connected() )
    {
        result =  -server_cmd( rmdir( server_path ) );

        if ( 0 == result )
            rmdir( flash_path );
    }
    else 
        result = -ENOTCONN;

    unlock_write();

    free( server_path );
    free( flash_path );

    if ( 0 != result && DEBUG )
        report_error( "rmdir: ", result );

    return result;
}

static int flache_release( const char * path, struct fuse_file_info * file_info)
{
    int handle = (int)file_info->fh;
    int copy_result = 0;
    int result = 0;

    lock_write();

    lock_list( file_list );

    write_output( "release" );

    struct file_entry_t* file = get_file( file_list, (char*)path );
    
    if ( NULL != file && contains_handle( file, handle ) )
    {
        write_output2( "release: ", file->path );

        int c = close( handle );

        remove_handle( file, handle );

        if ( 0 == c )
            write_output2( "closed: ", file->path );
        else
            write_output2( "couldn't close: ", file->path );

        // if all of the file handles are closed, then 
        // sync it to the server
        if ( file->handle_count == 0 && file->dirty )
        {
            copy_result = -server_cmd( copy_file( file->flash_path, file->server_path ) );
            
            // if the copy was successful, mark the file as clean
            file->dirty = !( 0 == copy_result );

            file->time_released = time( NULL );

            if ( 0 == copy_result )
                write_output2( "copied: ", file->path );
            else
                write_output2( "couldn't copy: ", file->path );
        }
        else if ( file->handle_count == 0 )
            file->time_released = time( NULL );
    }
    else 
        result = -EBADF; // bad file name

    if ( NULL != file )
        unlock_entry( file );

    unlock_list( file_list );

    unlock_write();

    return result;
}

static int flache_truncate(const char *path, off_t offset ) 
{
    int result = 0;
    char* server_path = build_path( flache_vol.server_path, path );
    char* flash_path = build_path( flache_vol.flash_path, path );

    if ( DEBUG )
        write_output2( "truncate: ", (char*)path );

    lock_write();
    
    if ( file_exists( flash_path ) )
        result = truncate( flash_path, offset );        
    else if ( is_connected() )
        result = -server_cmd( truncate( server_path, offset ) );
    else
        result = -ENOTCONN;

    // if the file on the disk was truncated, mark it as dirty
    if ( 0 == result && file_exists( flash_path ) )
    {
        // if truncated, we want the file to be syncd
        struct file_entry_t* entry = get_file( file_list, (char*)path );

        if ( entry == NULL )
        {
            lock_list( file_list );
            entry = create_entry( (char*)path, -1, &flache_vol );
            remove_handle( entry, -1 );
            entry->time_released = time( NULL );
            entry->dirty = TRUE;
            lock_entry( entry );
            add_file( file_list, entry );
            unlock_list( file_list);
        }

        // if we successfully truncated the server file, then
        // mark the local file as clean
        if ( 0 == -server_cmd( truncate( server_path, offset ) ) )
            entry->dirty = FALSE;

        unlock_entry( entry );
    }

    unlock_write();

    free( server_path );
    free( flash_path );

    if ( 0 != result )
        report_error( "truncate: ", result );

    return result;
}


static int flache_open( const char * path, struct fuse_file_info * file_info)
{
    int file_handle = 0;
    int result = 0;

    // get the full path to the real file
    char* server_path = build_path( flache_vol.server_path, path );
    char* flash_path = build_path( flache_vol.flash_path, path );
    struct file_entry_t* file = NULL;

    if( DEBUG )
        write_output2( "open: ", (char*)path );

    lock_open();
    
    if ( !file_exists( flash_path ) )
    { 
        if ( is_connected() )
            result = -server_cmd( copy_file( server_path, flash_path ) );
        else 
            result = -ENOTCONN;
    }

    if ( 0 == result )
    {
        lock_list( file_list );
        
        file = get_file( file_list, (char*)path );    

        // try to open the file
        file_handle = open( flash_path, file_info->flags );

        // the open was successful, add it to the list
        if ( file_handle >= 0 )
        {
            if ( NULL == file )
            {
                file = create_entry( (char*)path, file_handle, &flache_vol );
                lock_entry( file );
                add_file( file_list, file );
            }
            else
                add_handle( file, file_handle );

            file_info->fh = (uint64_t)file_handle;
        }

        result = file_handle;

        if ( NULL != file )
            unlock_entry( file );

        unlock_list( file_list );
    }

    unlock_open();

    if ( result < 0 )
        report_error( "open: ", result );

    free( server_path );    
    free( flash_path );
    
    return ( result > 0 ? 0 : result );
}

#if FUSE_VERSION >= 26
static void *flache_init(struct fuse_conn_info *conn)
#else
	static void *flache_init(void)
#endif
{
    flache_vol.is_connected = TRUE;

    // Initialize the locks for this file system
    pthread_mutexattr_init( &flache_vol.write_lock_attrib );
    pthread_mutexattr_settype( &flache_vol.write_lock_attrib, PTHREAD_MUTEX_RECURSIVE_NP );
    pthread_mutex_init( &flache_vol.write_lock, &flache_vol.write_lock_attrib );

    pthread_mutexattr_init( &flache_vol.open_lock_attrib );
    pthread_mutexattr_settype( &flache_vol.open_lock_attrib, PTHREAD_MUTEX_RECURSIVE_NP );
    pthread_mutex_init( &flache_vol.open_lock, &flache_vol.open_lock_attrib );

    file_list = create_entry_list();

    // scan_dir will populate the file_list with all files already on
    // the flash drive, and will try to sync them.
    scan_dir( "/" );

    // start the delete timer thread
    thread_info.volume = &flache_vol;
    thread_info.cancel = &cancel_delete_thread;
    thread_info.list = file_list;

    pthread_create( &timer_thread, NULL, delete_timer_start, (void*) &thread_info );

    // start the server check thread
    /*struct timer_thread_info server_check_thread_info;
    server_check_thread_info.volume = &flache_vol;
    server_check_thread_info.cancel = &cancel_server_check_thread;
    server_check_thread_info.list = file_list;*/
    
    pthread_create( &server_check_thread, NULL, monitor_server_status, (void*) &thread_info );//(void*) &server_check_thread_info );
    
    return NULL;
}

static void flache_destroy( void* data )
{
    // stop the delete timer thread
    cancel_delete_thread = TRUE;
    pthread_join( timer_thread, NULL );

    // stop the server check thread
    //cancel_server_check_thread = TRUE;
    pthread_join( server_check_thread, NULL );
}


static struct fuse_operations hello_oper = 
{
  .init = flache_init,
  .destroy = flache_destroy,
  .getattr   = flache_getattr,
  .readdir = flache_readdir,
  .opendir = flache_opendir,
  .mkdir = flache_mkdir,
  .chmod = flache_chmod,
  .mknod = flache_mknod,
  .rmdir = flache_rmdir,
  .open   = flache_open,
  .truncate = flache_truncate,
  .unlink = flache_unlink,
  .release = flache_release,
  .write = flache_write,
  .utime = flache_utime,
//.create = flache_create,
  .read   = flache_read,
  .rename = flache_rename
};

int main(int argc, char *argv[])
{
    // usage: ./flache mount-point sshfs-server flache-location
    if ( argc > 3 )
    {
        flache_vol.server_path = argv[argc - 2];
        flache_vol.flash_path = argv[argc - 1];

        printf( "server path: %s\n", flache_vol.server_path );
        printf( "flash path: %s\n", flache_vol.flash_path );
 
        return fuse_main(argc - 2, argv, &hello_oper, NULL);
    }
    else
        printf( "usage: %s mountpoint server_path flash_path\n", argv[0] );

    return 1;
}

